{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 实例5：用Python批量生成word版邀请函"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "每到年终时，公司都要举办年会，总结当年的得失。还有就是组织个晚会，表演些节目，抽奖......当然，还有，赏大餐一顿。人事部的妹子这时就发愁了，忙碌地进行各种准备不说，还要给供应商，政府部门发邀请函。这个邀请函其实除了受邀者的名字不同外，其它信息完全一样，纯粹的体力活。这种脏累活儿，就交给Python来干吧。\n",
    "\n",
    "首先，我们需要将所有受邀者的公司名和代表姓名填入一个Excel表格。然后做好邀请函的模板，在需要填字的地方打上小星星“****”，然后就让Python来玩填字游戏啦。话不多说，直入主题，代码走一波......\n",
    "![](images\\invitation_temp.PNG)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[' 供应商1 王波 ',\n",
       " ' 供应商2 刘海洋 ',\n",
       " ' 供应商3 少和光 ',\n",
       " ' 供应商4 真凡巧 ',\n",
       " ' 供应商5 鲁鸿运 ',\n",
       " ' 政府1 乐清霁 ',\n",
       " ' 政府2 郎绮琴 ',\n",
       " ' 政府3 贝芳懿 ',\n",
       " ' 政府4 哈彦君 ']"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#1.从Excel文件获取受邀人单位和名字\n",
    "from openpyxl import load_workbook\n",
    "wb=load_workbook('data/name_list.xlsx')\n",
    "ws=wb['name']\n",
    "names=[]\n",
    "for row in range(2,ws.max_row+1):\n",
    "    company=ws[\"A\"+str(row)].value\n",
    "    name=ws[\"B\"+str(row)].value\n",
    "    names.append(\" {} {} \".format(company,name))\n",
    "names"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们首先导入`openpyxl`模块中的`load_workbook`模块。`openpyxl`是一个可读取Excel的第三方库。`load_workbook()`可打开一个现有的Excel文件（或叫工作簿）。我们使用`load_workbook（path）`读取文件，并存在变量`wb`里面。我们可以想象变量就是容器，用来储存各种数据，这里的`wb`就是这个容器的名字。`wb`存储的是整个工作簿的内容，我们知道一个Excel有几个工作表“Sheet”，接下来我们需要选取我们所需要的“Sheet”，也就是名字叫\"name\"的那张表。当然你可以给这个工作表起自己喜欢的名字，在写代码的时候把`wb['name']`中的\"name\"字样替换成你的个性化名字即可。\n",
    "\n",
    "然后定义一个空列表`names`来存储受邀人单位和名字信息。随后我们遍历工作表，将所有信息提取出来存入列表`names`。因为工作表第一行是标题，所以我们从第2行开始，最大一行+1结束`range(2,ws.max_row+1)`。之所以要加1，是因为range(1,5)只能取到“1,2,3,4”四个值，5是取不到的。`ws.max_row`指工作表的总行数。然后我们开始提取公司名称,`company=ws[\"A\"+str(row)].value`，当row取2时，就相当于将工作表中A2（即A列2行单元格的值）单元格的值传给了company变量。获取姓名也是类似的操作方式。\n",
    "\n",
    "然后将公司名称和姓名排一下版，再加入names列表。`\" {} {} \".format(company,name)`这个格式化字符串函数就完成了这个操作。我们在公司名和姓名之间及前后都加入了一个空格，这样在最终的邀请函上看起来就是这样的：“尊敬的 供应商1 王波 先生/女士：”。不留空格的话，就变成这样了：“尊敬的供应商1王波先生/女士：”，似乎有点不和谐。格式化字符串可以理解为，将format(company,name)括号中的值依次填入前面的{}。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2.批量填字，存为不同的word文件\n",
    "import docx #导入处理word的库，无耻地直接使用别人造好的轮子\n",
    "doc=docx.Document('data/邀请函.docx')\n",
    "for name in names:\n",
    "    doc.paragraphs[1].runs[2].text=name\n",
    "    doc.save('data/邀请函_{}.docx'.format(name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "受邀人信息获取完成后，我们就可以开始批量生成邀请函了。首先导入处理word的库，无耻地直接使用别人造好的轮子。建立一个容器`doc`，来盛装打开的邀请函，`docx.Document('data/邀请函.docx')`为固定写法，括号内填写邀请函模板的路径及文件名。`doc`可理解为就是这个名为“邀请函”word文件。然后遍历`names`列表，将里面的信息逐个写入word文件的第二段的第三个run，即`doc.paragraphs[1].runs[2].text=name`，相当于直接用name的值“ 供应商1 王波 ”替换掉` **** `。其中的run指的是每一段中的不同的格式，比如一段中有常规的，有加粗的，有斜体的，有不同颜色的字，那么就有好几个run。\n",
    "\n",
    "那我们怎么知道` **** `是第二段第三个run呢？通过我们制作的“邀请函”模板，我们知道` **** `位于第二段(即`doc.paragraphs[1]`，因为是从0开始计数，所以0表示第一段，1就表示第二段)。请注意word中若有空行，也算一段，比如模板中的\"ABC有限公司\"开头的那一段已经算是第四段了。我么将第二段的所有run打印出来，就可以找到` **** `所在的run了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "尊敬\n",
      "的\n",
      " **** \n",
      "先生\n",
      "/\n",
      "女士\n",
      "：\n"
     ]
    }
   ],
   "source": [
    "doc=docx.Document('data/邀请函.docx')\n",
    "for run in doc.paragraphs[1].runs:\n",
    "    print(run.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以看到其中有7个runs，虽然“尊敬的”三个字格式是一样的，但是还是被识别为两个单独的run了，可能这个库主要用来处理英文文档，拿来搞中文有些水土不服。不管它了，让它乱识别吧。通过以上的run打印，我们简单粗暴地数下去，可以知道` **** `在第三个run，即`run[2]`。\n",
    "\n",
    "然后逐个保存为单个的文件，并在给新的word文件命名时加上受邀人的信息`doc.save('data/邀请函_{}.docx'.format(name))`。得到的结果如下：\n",
    "![](images\\result.PNG)\n",
    "![](images\\result_detail.PNG)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了方便打印，可以使用word自带的合并功能将所有邀请函合并为一个word文件。打开第一个邀请函word文档，然后单击【插入】选项卡下的【对象】菜单中的【文件中的文字】选项，选择剩余的所有邀请函word文件，点插入，搞定！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
